import { AnimationMixer, BufferGeometry, Clock, Mesh, MeshStandardMaterial, Vector3 } from "three";
import Entity, { EntityConfig } from "../Core/Entity";
import { RandomFromInterval, MaxDistance } from "../Utils/Util";
import { gsap } from "gsap";

export type NpcConfig = {
  type: string;
  scope?: {
    max: number;
    min: number;
  };
  animas?: {
    [x: string]: {
      i: number;
    };
  };
  collision: boolean;
} & EntityConfig;

export default class NPC extends Entity {
  config: NpcConfig;

  type: string;
  blood: number = 4;
  constructor(_config: NpcConfig) {
    super(_config);
    this.config = _config;
    this.type = this.config.type;
    this.behaviors.push(...["MeasurementFar", "Collision"]);
  }

  tl: gsap.core.Timeline;
  override async Builder() {
    await super.Builder();
    if (this.config.scope) this.SetPosition();
    if (this.config.animas) {
      for (const k in this.config.animas) {
        let m = this.config.animas[k];
        let clock = new Clock();
        let mixer = new AnimationMixer(this.model);
        mixer.clipAction(this.anims[m.i]).play();

        render.addClockEvent(k + this.id, () => {
          mixer.update(clock.getDelta());
        });
      }
    }
    this.Enter();
  }

  distance: number = Infinity;
  MeasurementFar() {
    let pos = this.model.getWorldPosition(new Vector3());
    let troopPos = world.troop.group.getWorldPosition(new Vector3());
    this.distance = pos.distanceTo(troopPos);

    if (this.distance > MaxDistance) {
      this.Exit();
    }
  }

  Collision() {
    if (this.blood > 0 && this.config.collision && this.distance < 3) {
      for (let i = 0; i < world.troop.group.children.length; i++) {
        let m = world.troop.group.children[i] as Mesh<BufferGeometry, MeshStandardMaterial>;
        if (m.userData.obb.intersectsOBB(this.model.userData.obb)) {
          m.userData.entity.Dispose();
        }
      }
    }
  }

  SetPosition() {
    let pos = new Vector3();
    let distance = 0;
    let { max, min } = this.config.scope;

    do {
      let x = RandomFromInterval(0, max, true),
        z = RandomFromInterval(0, max, true);

      pos.set(x, this.model.userData.pos.target.y, z).add(world.troop.group.position);
      let troopPos = world.troop.group.getWorldPosition(new Vector3());
      distance = pos.distanceTo(troopPos);

      this.model.position.copy(pos);
      this.UpdateOBB();
    } while (this.OverlapOther() || distance < min);
    this.model.position.y = this.model.userData.pos.origin.y;
  }

  OverlapOther() {
    for (let i = 0; i < world.Items.length; i++) {
      if (this.model.userData.obb.intersectsOBB(world.Items[i].value.model.userData.obb)) return true;
    }

    return false;
  }

  Enter() {
    this.tl = gsap.timeline({
      onComplete: () => {
        world.SetItem(this.model.position, { type: this.type, value: this });
      },
      onReverseComplete: () => {
        this.Dispose();
        this.tl.kill();
      },
    });
    this.tl.to(this.model.position, { duration: 0.5, ease: "power4.in", y: this.model.userData.pos.target.y });
    this.tl.to(this.model.rotation, { duration: 0.8, ease: "power4.in", y: Math.PI * 2 }, "<");
  }

  Exit() {
    world.DelItem(this);
    this.tl.reversed(true);
  }

  num: number = 0;
  UnderAttack() {
    this.blood--;
    this.num++;
    let unAttack = gsap.timeline({
      onComplete: () => {
        if (this.blood <= 0) {
          this.Exit();
        }
        this.num--;
        unAttack.kill();
      },
    });
    let n = 1 + this.num / 10;
    unAttack.to(this.model.scale, { duration: 0.3, x: n, y: n - 0.2, z: n });
    unAttack.to(this.model.scale, { duration: 0.3, x: 1, y: 1, z: 1 }, "<+.15");
  }
  Dispose() {
    super.Dispose();
  }
}
